package ai.people.netmon.auth.handler;

import ai.people.netmon.framework.exception.enums.AuthExceptionEnum;
import ai.people.netmon.framework.exception.enums.CommonEnum;
import ai.people.netmon.framework.exception.type.CustomizeRuntimeException;
import ai.people.netmon.framework.model.response.Result;
import ai.people.netmon.framework.utils.ResultUtil;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.util.Objects;

/**
 * 自定义web响应异常翻译
 *
 * @author yuanqinglong
 * @date 2022/4/14 14:55
 */
@Component
@RequiredArgsConstructor
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<Result<?>> {

    private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    private final Logger logger = LoggerFactory.getLogger(CustomWebResponseExceptionTranslator.class);


    private final AuthExceptionHandler authExceptionHandler;
    /**
     * 异常翻译
     *
     * @param e e
     * @return {@link ResponseEntity}<{@link Result}<{@link ?}>>
     * @throws Exception 异常
     */
    @Override
    public ResponseEntity<Result<?>> translate(Exception e) throws Exception {
        logger.error("authentication exception information：", e);
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e);
        CustomizeRuntimeException customizeRuntimeException = (CustomizeRuntimeException) this.throwableAnalyzer.getFirstThrowableOfType(CustomizeRuntimeException.class, causeChain);
        if (Objects.nonNull(customizeRuntimeException)) {
            return new ResponseEntity<>(ResultUtil.error(customizeRuntimeException), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return this.handlingAuthenticationExceptions(causeChain);
    }


    /**
     * 处理身份验证异常
     *
     * @param causeChain 导致链
     * @return {@link ResponseEntity}<{@link Result}<{@link ?}>>
     */
    private ResponseEntity<Result<?>> handlingAuthenticationExceptions(Throwable[] causeChain) {
        Exception ase = (InvalidGrantException) throwableAnalyzer.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
        if (ase != null) {
            return new ResponseEntity<>(ResultUtil.error(AuthExceptionEnum.AUTH_ACCOUNT_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        ase = (InvalidTokenException) throwableAnalyzer.getFirstThrowableOfType(
                InvalidTokenException.class, causeChain);
        if (ase != null) {
            return new ResponseEntity<>(ResultUtil.error(AuthExceptionEnum.INVALID_TOKEN), HttpStatus.INTERNAL_SERVER_ERROR);
        }
         ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);

        if (ase != null) {
            return new ResponseEntity<>(ResultUtil.error(AuthExceptionEnum.INVALID_CREDENTIALS), HttpStatus.INTERNAL_SERVER_ERROR);
        }

        ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,
                causeChain);
        if (ase != null) {
            Result<?> result = authExceptionHandler.authenticationException((AuthenticationException) ase);
            return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase != null) {
            return new ResponseEntity<>(ResultUtil.error(AuthExceptionEnum.INSUFFICIENT_PERMISSIONS), HttpStatus.INTERNAL_SERVER_ERROR);
        }

        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(
                HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase != null) {
            return new ResponseEntity<>(ResultUtil.error(CommonEnum.REQUEST_METHOD_TYPE_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<>(ResultUtil.error(AuthExceptionEnum.AUTHENTICATION_FAILED), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
